from django.db import models
from django_fsm import FSMField, transition, RETURN_VALUE, GET_STATE
from datetime import datetime, date


class BlogPostStateEnum(object):
    NEW = '10'
    PUBLISHED = '20'
    HIDDEN = '30'
    STOP = '40'
    Failed = '500'


def can_publish(instance):
    # 添加一些状态转移的逻辑和限定
    # No publishing after 17 hours
    print(type(datetime.now().hour))
    if datetime.now().hour > 10:
        print('return false')
        return False
    return True


class DbState(models.Model):
    """
    这样可以让状态值放在数据库
    {
        "pk": "new",
        "model": "myapp.dbstate",
        "fields": {
            "label": "_NEW_"
        }
    }
    """
    id = models.CharField(primary_key=True, max_length=50)
    label = models.CharField(max_length=255)

    def __unicode__(self):
        return self.label


class BlogPost(models.Model):
    state = FSMField(default=BlogPostStateEnum.NEW)  # 保存默认是string
    # state = FSMField(DbState, default='new)
    # 这样不能直接修改状态值 m.state = 'invalid' Raises AttributeError
    # state = FSMField(default='new', protected=True)
    name = models.CharField(max_length=30)
    context = models.TextField()
    published_at = models.DateTimeField(null=True)
    updated_at = models.DateTimeField(null=True)
    created_at = models.DateTimeField(auto_now_add=True)

    # 使用装饰器，限定状态转移，还可以添加转移的条件在conditions
    # @transition(field=state, source=BlogPostStateEnum.NEW, target=BlogPostStateEnum.PUBLISHED)
    @transition(field=state, source=BlogPostStateEnum.NEW, target=BlogPostStateEnum.PUBLISHED, conditions=[can_publish])
    def publish(self):
        self.published_at = datetime.now()
        self.updated_at = datetime.now()

    @transition(
        field=state,
        source=BlogPostStateEnum.PUBLISHED,
        target=BlogPostStateEnum.HIDDEN,
        custom=dict(verbose='Hold for legal reasons'),  # 没有理解用途
        on_error=BlogPostStateEnum.Failed  # 当过程出错的时候，转到这个状态
    )
    def hided(self):
        self.updated_at['x'] = 0
        self.updated_at = datetime.now()

    @transition(field=state,
                source='*',  # 多种条件匹配
                target=RETURN_VALUE(BlogPostStateEnum.STOP, BlogPostStateEnum.HIDDEN, BlogPostStateEnum.PUBLISHED))
    def change(self, is_public=False):
        if self.state == BlogPostStateEnum.PUBLISHED:
            return BlogPostStateEnum.HIDDEN if is_public else BlogPostStateEnum.STOP
        else:
            return BlogPostStateEnum.PUBLISHED

    @transition(field=state, source='*', target=BlogPostStateEnum.STOP,
                permission='myOrder.can_remove_BlogPost')
    def remove(self):
        self.updated_at = datetime.now()
        return 'remote post'
